Skip to content

feat(frontend): add context window usage tracking#825

Closed
jeremyeder wants to merge 2 commits intomainfrom
feat/context-usage-indicator
Closed

feat(frontend): add context window usage tracking#825
jeremyeder wants to merge 2 commits intomainfrom
feat/context-usage-indicator

Conversation

@jeremyeder
Copy link
Contributor

@jeremyeder jeremyeder commented Mar 5, 2026

Summary

  • Track cumulative token usage from RUN_FINISHED events
  • Display as sparkline showing tokens per turn
  • Dynamic context limits based on model (1M for opus-4, 200K for others)

Changes

File Change
types/agui.ts Add contextUsage, PlatformRunFinishedEvent, getContextLimit()
hooks/agui/types.ts Initialize contextUsage in initialState
hooks/agui/event-handlers.ts Extract usage from RUN_FINISHED.result
components/session/ContextUsage.tsx New component

Usage

import { ContextUsage } from '@/components/session/ContextUsage'

<ContextUsage state={aguiState} model={session.spec.llmSettings.model} />

Renders: 83K/200K ▁▂▃▄▂▃▁▂▄▃▂▄

Context Limits

Model Limit
claude-opus-4* 1M tokens
claude-sonnet*, claude-haiku* 200K tokens

Test plan

  • Verify tokens accumulate correctly across turns
  • Check sparkline renders with correct bar heights
  • Confirm 1M limit for opus models, 200K for others
  • Confirm no errors when result.usage is missing

🤖 Generated with Claude Code

@jeremyeder jeremyeder force-pushed the feat/context-usage-indicator branch from 23dd182 to 5a02810 Compare March 5, 2026 19:59
@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

placeholder

@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

hello

@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Test line one
Test line two
Test line three

@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Test Header

Some content

@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Claude Code Review

Summary

This PR adds context window usage tracking via RUN_FINISHED events and a sparkline component. Clean implementation overall, but contains one standards violation and two functional correctness issues to address before merge.

Blocker Issues: None.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Claude Code Review

CRITICAL: Use type not interface. ContextUsage.tsx line 16.

MAJOR 1: Accumulation bug. event-handlers.ts lines 279-288.

MAJOR 2: Hardcoded limit 200K. types.ts line 50.

MAJOR 3: Unbounded perTurn. event-handlers.ts line 287.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Claude Code Review - Part 1 of 4: Summary and Critical Issues

Summary: This PR adds context window usage tracking from RUN_FINISHED events with a sparkline display. Clean implementation overall with one standards violation and two correctness issues.

CRITICAL - interface instead of type for props
File: components/frontend/src/components/session/ContextUsage.tsx line 16
Problem: "interface ContextUsageProps" violates the zero-tolerance no-interface rule.
Standard: frontend-development.md - "Use type Over interface - REQUIRED: Always prefer type"
Fix: change "interface ContextUsageProps" to "type ContextUsageProps ="

@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Claude Code Review - Part 2 of 4: Major Issues

MAJOR 1 - Token accumulation semantics are incorrect
File: components/frontend/src/hooks/agui/event-handlers.ts lines 279-288
Problem: The code accumulates "used + (input_tokens + output_tokens)" across turns. In the Anthropic API, input_tokens for turn N already includes all prior conversation history. Accumulating these values double-counts previous context causing "used" to exceed "limit" and "percent" to exceed 100% after just a few turns. This makes the feature display incorrect data from the second turn onward.
Fix: Replace the accumulated "used" with input_tokens from the most recent turn only. Use output_tokens per-turn for the sparkline bars instead.

MAJOR 2 - Hardcoded context limit ignores model selection
File: components/frontend/src/hooks/agui/types.ts line 50
Problem: limit: 200_000 is hardcoded. Claude models have varying context windows. The percent calculation and "X/200K" label will be wrong for non-200K models.
Fix: Drive the limit from the session model configuration, or add a TODO comment.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Claude Code Review - Part 3 of 4: Remaining Issues

MAJOR 3: perTurn grows unbounded. Cap the array on each write to prevent long sessions from accumulating thousands of state entries.

MINOR 1: Array index as React key in sparkline bars - use a stable key derived from position in the full perTurn array.

MINOR 2: Per frontend-development.md single-use components should be colocated with their page. Please confirm whether ContextUsage is only used in one place.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Recommendations: 1 Fix token accumulation. 2 Fix interface to type. 3 Cap perTurn array. 4 Address hardcoded limit. 5 Fix array index key.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

Positives: Correct unknown-cast pattern for event assertions. Inline object type in agui.ts is correct. Graceful no-op when usage data is absent. Division-by-zero guard in sparkline. Clean formatTokens helper.

@ambient-code
Copy link

ambient-code bot commented Mar 5, 2026

Merge Readiness — Blockers Found\n\n| Check | Status | Detail |\n|-------|--------|--------|\n| CI | FAIL | Failing: lint-frontend, build-and-push (frontend, ./components/frontend, quay.io/ambient_code/vteam_frontend, ./component..., lint-summary (+1 more) |\n| Merge conflicts | pass | — |\n| Review comments | needs_review | Has comments — agent to evaluate |\n| Jira hygiene | warn | No Jira reference found |\n| Staleness | pass | — |\n| Diff overlap risk | pass | — |\n\n> This comment is auto-generated by the PR Overview workflow and will be updated when the PR changes.\n\n

Track cumulative token usage from RUN_FINISHED events and display
as a sparkline indicator showing tokens per turn.

- Add contextUsage field to AGUIClientState
- Add PlatformRunFinishedEvent type with result.usage shape
- Add getContextLimit() utility for dynamic model-based limits
- Extract input_tokens + output_tokens from RUN_FINISHED result
- Add ContextUsage component with token count and sparkline

Context limits:
- claude-opus-4*: 1M tokens
- claude-sonnet*, claude-haiku*: 200K tokens (default)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@jeremyeder jeremyeder force-pushed the feat/context-usage-indicator branch from b064f7b to 8848df4 Compare March 6, 2026 01:30
@github-actions

This comment has been minimized.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 6, 2026

Claude Code Review for PR 825 - context-usage-indicator

@github-actions
Copy link
Contributor

github-actions bot commented Mar 6, 2026

Claude Code Review

Summary

This PR introduces context window usage tracking for the frontend session UI. It adds token accumulation in the RUN_FINISHED event handler, a new ContextUsage sparkline component, and model-aware context limit lookup. The implementation is clean and well-typed. One functional accuracy issue with the Opus 4 context limit and a few minor style points are worth addressing.

Blocker Issues: None

Critical Issues: None

Major Issues

  1. Incorrect context window limit for Claude Opus 4

File: components/frontend/src/types/agui.ts (line ~296)

Problem: claude-opus-4 is mapped to 1,000,000 tokens, but Claude Opus 4 (including claude-opus-4-6, the model ID this platform defaults to) has a 200K context window, the same as Sonnet and Haiku. The prefix check model.startsWith(claude-opus-4) will match claude-opus-4-6 and show 83K/1M instead of the correct 83K/200K, making the indicator actively wrong for the default model.

Standard violated: Code quality, functional correctness of the core feature.

Suggested fix: Verify against Anthropic published context limits. If all Claude 4.x models are 200K, remove the Opus 1M entry. If a 1M variant genuinely exists, use its exact model ID prefix rather than the generic claude-opus-4 prefix.

Minor Issues

  1. perTurn array grows unbounded (event-handlers.ts line ~281): Every turn appends without a cap. Display is limited to slice(-24) but state grows indefinitely. Fix: add .slice(-50) when building the array.

  2. Hardcoded bg-green-500 in sparkline (ContextUsage.tsx line 40): Does not adapt to dark mode or the Shadcn token system. Use a semantic token like bg-primary/70 instead.

  3. Array index as React key (ContextUsage.tsx line 38): key={i} is acceptable for a visual shifting list but is a minor pattern deviation. Consider a composite key or add a comment.

  4. getContextLimit utility in types file (agui.ts line ~290+): Runtime logic does not belong in a types file. Move CONTEXT_LIMITS and getContextLimit to src/utils/model.ts.

Positive Highlights

Clean TypeScript throughout: no any anywhere in the diff, all new types use the type keyword.

Excellent type extension pattern: PlatformRunFinishedEvent = RunFinishedEvent and { result?: ... } augments the base type cleanly without forking.

Graceful degradation: getContextLimit falls back to DEFAULT_CONTEXT_LIMIT and the event handler guards on result?.usage, so missing usage data never crashes.

Sensible UX: capping the sparkline to 24 turns and guarding on recent.length > 0 keep the display clean from the start.

formatTokens handles raw, K, and M scale tiers correctly and concisely.

Recommendations

  1. (Must) Correct the Opus 4 context limit before merge. The feature core display is wrong for the default platform model.
  2. (Should) Cap perTurn at 50 entries to prevent unbounded state growth in long sessions.
  3. (Nice to have) Replace bg-green-500 with a semantic Shadcn/Tailwind color token.
  4. (Nice to have) Move getContextLimit to a utils/ file to keep the types file focused on type definitions.

The ContextUsage component was implemented but never imported or rendered
in the session UI. This adds the missing import and renders the component
in the header area next to the session kebab menu.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@github-actions
Copy link
Contributor

github-actions bot commented Mar 6, 2026

Claude Code Review

Summary

This PR adds context window usage tracking to the session UI, including a new ContextUsage component with a sparkline visualization, a PlatformRunFinishedEvent type extension, and context accumulation logic in the AG-UI event handler. The implementation is clean TypeScript with proper types throughout. One meaningful semantic bug affects the correctness of the primary displayed metric; a few minor style points are also noted.

Issues by Severity

Blocker Issues

None

Critical Issues

None

Major Issues

1. contextUsage.used accumulates tokens across turns and will exceed the context limit — misleading display

  • File: components/frontend/src/hooks/agui/event-handlers.ts (lines 282–289) and components/frontend/src/components/session/ContextUsage.tsx (line 30)
  • Problem: used is a cumulative sum of input_tokens + output_tokens across every RUN_FINISHED event. Because input_tokens in LLM APIs represents the full prompt sent each turn (including the entire prior conversation), summing it across turns produces a number that inflates rapidly and will routinely exceed the context limit — e.g., a 10-turn session with 8K input tokens per turn gives used = 80K+ vs. a 200K limit. The UI renders {formatTokens(used)}/{formatTokens(limit)}, which reads to users as "tokens currently in the context window / context limit". A value that can exceed the limit makes the ratio nonsensical and the display misleading.
  • Standard violated: frontend-development.md — Loading and error states handled; more broadly, accuracy of UI data presented to users.
  • Suggested fix: Use the input_tokens from the most recent run as used, since input_tokens for that turn already represents the entire conversation context sent to the model:
if (event.result?.usage) {
  const { input_tokens = 0, output_tokens = 0 } = event.result.usage

  state.contextUsage = {
    // input_tokens of the last turn == current context window occupancy
    used: input_tokens,
    perTurn: [...state.contextUsage.perTurn, input_tokens + output_tokens],
  }
}

The sparkline (perTurn) can still store combined tokens per turn for per-turn activity visualization, while used correctly reflects current context occupancy.

Minor Issues

1. Array index used as React key

  • File: components/frontend/src/components/session/ContextUsage.tsx (line 40)
  • Problem: key={i} (array index) is used for sparkline bars. While the sparkline only appends and the bars don't reorder, React's reconciler will incorrectly diff on insert/remove if the leading items ever change. A more stable key is the turn index from the full perTurn array.
  • Suggested fix:
// Compute start offset before the map
const offset = state.contextUsage.perTurn.length - recent.length
// ...
{recent.map((tokens, i) => (
  <div key={offset + i} ... />
))}

2. ContextUsage component placed in shared directory despite single-page usage

  • File: components/frontend/src/components/session/ContextUsage.tsx
  • Problem: Per the colocate-single-use-components rule (frontend-development.md), components used in only one page should live in that page's _components/ directory. ContextUsage is currently only consumed by [sessionName]/page.tsx.
  • Standard violated: frontend-development.md — Single-use components colocated with their page.
  • Suggested fix: Move to app/projects/[name]/sessions/[sessionName]/_components/ContextUsage.tsx unless the component is expected to be reused imminently. (Note: MessagesTab and SessionStartingEvents follow the same pattern as this PR, so this is an existing area of drift — worth fixing here or filing a follow-up.)

Positive Highlights

  • Fully typed, zero any: All new code uses proper TypeScript types. ResultUsage, PlatformRunFinishedEvent, ContextUsageProps, and AGUIClientState.contextUsage are all correctly typed.
  • type over interface throughout: Every new type definition uses type, consistent with project standards.
  • Clean event-handler pattern: The usage extraction in handleRunFinished is idiomatic — optional chaining on event.result?.usage, destructuring with defaults, and immutable state update.
  • Sparkline is Tailwind-native: Dynamic bar heights via inline style are the correct approach where Tailwind can't express runtime values — no unnecessary dependencies added.
  • Graceful null handling: The component correctly guards recent.length > 0 before rendering the sparkline, and Math.max(...recent, 1) prevents a divide-by-zero.
  • getContextLimit prefix matching is correct: All currently shipping model families are handled, and the 200K default is the right safe fallback.

Recommendations

  1. (Fix before merge) Correct contextUsage.used to store input_tokens from the most recent turn rather than a running cumulative sum. The PR description says "cumulative" but the denominator display (/200K) only makes sense as current context occupancy.
  2. (Follow-up) Move ContextUsage.tsx to the session page's _components/ directory per colocate rules, or open a tracking issue to address the broader pattern drift in components/session/.
  3. (Follow-up) Stabilize the sparkline key to be turn-offset-based rather than render-index-based.

🔍 View AI decision process (logs available for 90 days)

📋 View memory system files loaded (click to expand)

What Amber Loaded for Code Review

Amber automatically loaded these repository standards from the memory system:

  1. CLAUDE.md - Master project instructions, development standards
  2. backend-development.md - Go backend, K8s integration patterns
  3. frontend-development.md - NextJS, Shadcn UI, React Query patterns
  4. security-standards.md - Auth, RBAC, token handling
  5. k8s-client-usage.md - User token vs service account patterns
  6. error-handling.md - Consistent error patterns
  7. react-query-usage.md - Data fetching patterns

Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines.

@jeremyeder
Copy link
Contributor Author

a wreck of an implementation attempt. i stopped caring about this since switching to the 1m context 4.6

@jeremyeder jeremyeder closed this Mar 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant